CSRF 攻击
CSRF 攻击:原理、危害与防御方案(深度解析)
一、CSRF 攻击的本质
CSRF(Cross-Site Request Forgery)跨站请求伪造,指攻击者诱导用户在已登录的状态下,无意中向目标网站发送恶意请求,利用用户的身份执行非自愿操作(如转账、修改密码等)。
核心原理:利用浏览器自动携带 cookie 等身份凭证的机制,结合用户对恶意网站的信任。
二、攻击流程与典型场景
攻击三要素
- 用户已登录目标网站并保持会话
- 攻击者构造携带目标网站请求的恶意页面
- 用户访问恶意页面时触发请求
攻击流程图解
1. 用户登录银行网站A,获取认证Cookie → [用户浏览器] + [网站A Cookie]
2. 攻击者创建恶意网站B,内含隐藏的转账请求 → <img src="https://bankA/transfer?to=hacker&amt=1000">
3. 用户访问网站B时,浏览器自动向网站A发送请求并携带Cookie → 网站A误认为请求合法,执行转账
常见攻击场景
- 自动提交表单:恶意页面通过
<form>
标签自动提交到目标网站 - 图片/链接诱导:伪装成图片的请求(如
<img src="...">
) - 钓鱼邮件:诱导用户点击含恶意请求的链接
三、CSRF 攻击的危害
- 账户安全:资金盗窃、信息篡改(如修改用户资料、绑定恶意账号)
- 数据泄露:诱导用户发送敏感信息请求(如获取订单列表)
- 业务破坏:批量执行恶意操作(如刷票、恶意注册)
四、防御方案(多层防护体系)
方案一:同源策略强化(基础防护)
原理:限制非同源请求的执行,但浏览器默认同源策略对 GET 请求限制较弱。
实现:
- 后端验证请求来源的
Origin
或Referer
头部 - 仅允许白名单域名的请求(需注意跨域场景)
# 示例:Django 中验证 Referer(伪代码)
def transfer(request):
referer = request.META.get('HTTP_REFERER', '')
if not referer.startswith('https://yourbank.com'):
return HttpResponseForbidden("非法请求")
方案二:CSRF Token 机制(核心防护)
原理:在请求中加入随机令牌,后端验证令牌合法性。
实现步骤:
- 服务器生成随机 Token 并存储(如 Session 或 Cookie)
- 页面表单或请求中携带 Token(如隐藏表单字段或请求头)
- 后端验证请求中的 Token 与存储的一致性
<!-- 前端表单携带 Token -->
<form action="/transfer" method="post">
<input type="hidden" name="csrf_token" value="随机令牌">
<input type="text" name="amount">
<button type="submit">转账</button>
</form>
// 后端验证示例(Spring Security)
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
方案三:SameSite Cookie(现代浏览器方案)
原理:通过 Cookie 的 SameSite
属性限制跨站请求携带 Cookie。
属性值:
Strict
:完全禁止跨站请求携带 CookieLax
:允许部分安全的跨站请求(如 GET 方法、顶级导航)None
:允许跨站请求(需配合Secure
属性启用 HTTPS)
Set-Cookie: session_id=12345; SameSite=Lax; Secure; HttpOnly
方案四:双重 Cookie 验证(增强防护)
原理:结合 Cookie 和请求参数中的 Token,利用浏览器跨站无法修改 Cookie 的特性。
实现:
- 服务器在 Cookie 中存储 Token(如
X-CSRF-TOKEN
) - 请求头或参数中携带相同 Token
- 后端验证 Cookie 与请求中的 Token 一致性
// 前端自动获取 Cookie 并添加到请求头
fetch('/api/action', {
method: 'POST',
headers: {
'X-CSRF-Token': document.cookie.match(/X-CSRF-TOKEN=([^;]+)/)[1]
}
})
五、各防御方案对比
方案 | 核心原理 | 优点 | 缺点 | 兼容性 |
---|---|---|---|---|
同源策略验证 | 验证请求来源域名 | 实现简单 | 依赖浏览器,可被伪造 Referer | 全兼容 |
CSRF Token | 随机令牌双向验证 | 防护全面,兼容性好 | 需前端后端配合 | 全兼容 |
SameSite Cookie | 限制跨站 Cookie 携带 | 浏览器原生支持,无额外开销 | iOS 12 以下、老版本浏览器不支持 | 现代浏览器 |
双重 Cookie 验证 | 结合 Cookie 和请求 Token | 无需服务器存储状态 | 需 JavaScript 支持 | 现代浏览器 |
六、实战优化建议
-
分级防护:
- 核心功能(如转账、修改密码)必须同时启用 Token 和 SameSite Cookie
- 普通功能可仅用 Token 或 SameSite=Lax
-
防御绕过攻击:
- 禁止 GET 请求执行敏感操作(如删除数据)
- 对敏感操作添加二次验证(如短信验证码)
-
框架集成:
- Spring Security、Django、Ruby on Rails 等框架内置 CSRF 防护,需正确配置
- 前端框架(如 Vue、React)可通过插件自动注入 Token
七、总结:CSRF 防御最佳实践
- 必选方案:CSRF Token + SameSite=Lax Cookie
- 增强方案:双重 Cookie 验证 + 敏感操作二次验证
- 开发规范:
- 避免在 URL 中携带敏感参数(如
?action=delete
) - 区分 GET(查询)和 POST/PUT/DELETE(修改)请求语义
- 定期更新 Token 生成算法和密钥
- 避免在 URL 中携带敏感参数(如
通过多层防护体系,可有效抵御 99% 以上的 CSRF 攻击,同时保证用户体验不受影响。